#include <stdio.h>
#include <stdbool.h>
#include <malloc.h>
#include "clib_memory_static_fixed_pool.h"
#include "../../base/bits/clib_base_bits.h"

/**
 * 内存池的对齐长度
 */
#define CLIB_STATIC_FIXED_POOL_DATA_ALIGN  CLIB_CPU_BIT_BYTE


struct clib_memory_static_fixed_pool {
    //是否是自动申请的内存空间，如果是自动申请的，销毁内存池时需要释放
    bool is_alloc;
    //真实数据的开始指针
    clib_uint8_t *data_point;
    //数据末尾指针
    clib_uint8_t *data_tail_point;
    //使用的信息
    clib_uint8_t *used_info;
    //使用的信息大小
    size_t used_info_size;
    //预测的索引
    size_t predict_index;
    //每个元素的数据大小
    size_t per_item_data_size;
    //每个元素的总大小
    size_t per_item_total_size;
    //当前元素的个数
    size_t current_item_count;
    //元素的最大个数
    size_t max_item_count;
    //数据的头长度
    clib_uint16_t data_head_size;
    //读写锁
    pthread_rwlock_t lock;
};


static struct clib_memory_static_fixed_pool *
clib_memory_static_fixed_pool_create__(clib_uint8_t *pool_data, size_t pool_size, size_t item_size, size_t item_count,
                                       bool is_alloc) {
    clib_check_if_true_return_value(pool_data == NULL, NULL);
    clib_check_if_true_return_value(pool_size == 0, NULL);
    clib_check_if_true_return_value(item_size == 0, NULL);

    //对齐数据和大小
    //获取pool_data指针对齐之后的指针偏移量
    size_t size_diff = clib_align(pool_data, CLIB_STATIC_FIXED_POOL_DATA_ALIGN) - (size_t) pool_data;
    //如果(指针偏移量 + 结构体长度)比池的大小还要小，则没有空间可以利用，初始化失败
    clib_check_if_true_return_value((size_diff + sizeof(struct clib_memory_static_fixed_pool)) >= pool_size, NULL);
    //将池的大小变为对齐之后的大小
    pool_size -= size_diff;
    //将池的开始指针指向到对齐之后的内存地址
    pool_data += size_diff;

    //初始化池子
    struct clib_memory_static_fixed_pool *pool = (struct clib_memory_static_fixed_pool *) (pool_data);
    memset(pool, 0, sizeof(struct clib_memory_static_fixed_pool));

    //设置数据头的长度
    pool->data_head_size = 0;
    //设置每个元素占用的总空间并对齐
    pool->per_item_total_size = pool->data_head_size + item_size;
    pool->per_item_total_size = clib_align(pool->per_item_total_size, CLIB_STATIC_FIXED_POOL_DATA_ALIGN);

    //初始化使用的信息
    //将使用的信息指针指向数据池头数据的后面
    pool->used_info = (clib_uint8_t *) clib_align(&pool[1], CLIB_STATIC_FIXED_POOL_DATA_ALIGN);
    //判断使用的信息是否超出了地址范围
    clib_check_if_true_return_value(pool_data + pool_size <= pool->used_info, NULL);
    //计算出能容纳元素的最大个数
    pool->max_item_count =
            (((pool_data + pool_size - pool->used_info) << 3) - 7) / (1 + (pool->per_item_total_size << 3));
    clib_check_if_true_return_value(pool->max_item_count == 0, NULL);
    if (item_count != 0) {
        if (pool->max_item_count > item_count) {
            pool->max_item_count = item_count;
        }
    }
    //计算使用信息的大小
    pool->used_info_size = clib_align(pool->max_item_count, CLIB_CPU_BIT_SIZE) >> 3;

    //清空使用信息
    memset(pool->used_info, 0, pool->used_info_size);


    //初始化数据指针,指向used info数据后面
    pool->data_point = (clib_uint8_t *) clib_align(pool->used_info + pool->used_info_size,
                                                   CLIB_STATIC_FIXED_POOL_DATA_ALIGN);
    //检查数据指针是否越界
    clib_check_if_true_return_value(pool_data + pool_size < pool->data_point, NULL);
    //检查数据空间是否异常
    clib_check_if_true_return_value(pool->max_item_count * pool->per_item_total_size >
                                    (size_t) (pool_data + pool_size - pool->data_point + 1), NULL);

    //设置数据尾部指针
    pool->data_tail_point = pool->data_point + pool->max_item_count * pool->per_item_total_size;
    //每个元素的真实数据大小
    pool->per_item_data_size = item_size;
    //目前元素的个数
    pool->current_item_count = 0;
    //目前预测的索引
    pool->predict_index = 1;
    //设置标记
    pool->is_alloc = is_alloc;

    //初始化锁对象
    pthread_rwlock_init(&pool->lock, NULL);
    return pool;
}

struct clib_memory_static_fixed_pool *
clib_memory_static_fixed_pool_create(clib_uint8_t *pool_data, size_t pool_size, size_t item_size) {
    return clib_memory_static_fixed_pool_create__(pool_data, pool_size, item_size, 0, false);
}

struct clib_memory_static_fixed_pool *
clib_memory_static_fixed_pool_auto_create(size_t item_size, size_t item_count) {
    clib_check_if_true_return_value(item_size == 0, NULL);
    item_size = clib_align(item_size, CLIB_STATIC_FIXED_POOL_DATA_ALIGN);
    size_t data_size = clib_align(item_size * item_count, CLIB_STATIC_FIXED_POOL_DATA_ALIGN);
    size_t pool_header_size = clib_align(sizeof(struct clib_memory_static_fixed_pool),
                                         CLIB_STATIC_FIXED_POOL_DATA_ALIGN);
    size_t used_info_size = (clib_align(item_count, CLIB_CPU_BIT_SIZE) >> 3);
    size_t pool_size = pool_header_size + data_size + used_info_size + CLIB_STATIC_FIXED_POOL_DATA_ALIGN;
    pool_size = clib_align(pool_size, CLIB_STATIC_FIXED_POOL_DATA_ALIGN);
    clib_uint8_t *pool_data = clib_memory_allocator_malloc_default(pool_size);
    return clib_memory_static_fixed_pool_create__(pool_data, pool_size, item_size, item_count, true);
}

bool
clib_memory_static_fixed_pool_is_full(struct clib_memory_static_fixed_pool *pool) {
    clib_check_if_true_return_value(pool == NULL, 0);
    return pool->max_item_count == pool->current_item_count ? true : false;
}

#ifdef CLIB_WORDS_BIGENDIAN

static size_t clib_memory_static_fixed_pool_find_used_info_first_free_index(size_t x) {
    return clib_bits_find_bit0_size_be(x);
}

#else

static size_t clib_memory_static_fixed_pool_find_used_info_first_free_index(size_t x) {
    return clib_bits_find_bit0_size_le(x);
}

#endif

/**
 * 判断使用信息中 指定索引是否已经标记为1，即已经使用过
 * @param used_info 标记信息
 * @param index 索引
 * @return true 说明使用过 false 没有使用过
 */
static bool clib_memory_static_fixed_pool_is_used_index(const clib_uint8_t *used_info, size_t index) {
    return ((used_info)[(index) >> 3] & (0x1 << ((index) & 7)));
}

/**
 * 将指定索引设置为已经使用状态
 * @param used_info
 * @param index
 */
static void clib_memory_static_fixed_pool_set_index_alloc(clib_uint8_t *used_info, size_t index) {
    (used_info)[(index) >> 3] |= (0x1 << ((index) & 7));
}

/**
 * 将指定索引设置为空闲状态
 * @param used_info
 * @param index
 */
static void clib_memory_static_fixed_pool_set_index_free(clib_uint8_t *used_info, size_t index) {
    (used_info)[(index) >> 3] &= ~(0x1 << ((index) & 7));
}


void *clib_memory_static_fixed_pool_malloc(struct clib_memory_static_fixed_pool *pool) {
    clib_check_if_true_return_value(pool == NULL, NULL);
    //判断是否已经满了
    bool is_full = clib_memory_static_fixed_pool_is_full(pool);
    clib_check_if_true_return_value(is_full, NULL);

    //加锁
    pthread_rwlock_wrlock(&pool->lock);

    size_t *used_info_point = (size_t *) pool->used_info;
    size_t *data_point = (size_t *) (pool->used_info + pool->used_info_size);
    while (used_info_point + 7 < data_point) {
        if (used_info_point[0] + 1) {
            used_info_point += 0;
            break;
        }
        if (used_info_point[1] + 1) {
            used_info_point += 1;
            break;
        }
        if (used_info_point[2] + 1) {
            used_info_point += 2;
            break;
        }
        if (used_info_point[3] + 1) {
            used_info_point += 3;
            break;
        }
        if (used_info_point[4] + 1) {
            used_info_point += 4;
            break;
        }
        if (used_info_point[5] + 1) {
            used_info_point += 5;
            break;
        }
        if (used_info_point[6] + 1) {
            used_info_point += 6;
            break;
        }
        if (used_info_point[7] + 1) {
            used_info_point += 7;
            break;
        }
        used_info_point += 8;
    }
    while (used_info_point < data_point && !(*used_info_point + 1)) used_info_point++;

    // 找到一个可用的索引
    size_t max_item_count = pool->max_item_count;
    size_t find_index = (((clib_uint8_t *) used_info_point - pool->used_info) << 3) +
                        clib_memory_static_fixed_pool_find_used_info_first_free_index(*used_info_point);
    //判断找到的索引是否超出了最大值
    clib_check_if_true_exec(find_index >= max_item_count) {
        pthread_rwlock_unlock(&pool->lock);
        return NULL;
    }
    //获取返回的数据指针
    clib_uint8_t *data_head_point = pool->data_point + find_index * pool->per_item_total_size;
    //将索引标记位设置为1
    clib_memory_static_fixed_pool_set_index_alloc(pool->used_info, find_index);

    //计算真正数据位置
    clib_uint8_t *return_data = data_head_point + pool->data_head_size;
    //当前已经使用个数+1
    pool->current_item_count += 1;
    pthread_rwlock_unlock(&pool->lock);
    return return_data;
}


bool clib_memory_static_fixed_pool_free(struct clib_memory_static_fixed_pool *pool, void *data) {
    clib_check_if_true_return_value(pool == NULL, false);
    clib_check_if_true_return_value(pool->per_item_data_size == 0, false);

    pthread_rwlock_wrlock(&pool->lock);

    clib_uint8_t *free_data = (clib_uint8_t *) data - pool->data_head_size;
    //计算索引
    size_t free_index = (free_data - pool->data_point) / pool->per_item_total_size;

    clib_check_if_true_exec(free_data < pool->data_point){
        pthread_rwlock_unlock(&pool->lock);
        return NULL;
    }
    clib_check_if_true_exec(free_data + pool->per_item_total_size > pool->data_tail_point){
        pthread_rwlock_unlock(&pool->lock);
        return NULL;
    }
    clib_check_if_true_exec(pool->current_item_count == 0){
        pthread_rwlock_unlock(&pool->lock);
        return NULL;
    }

    if (!clib_memory_static_fixed_pool_is_used_index(pool->used_info, free_index)) {
        printf("the data of index is unused, can not free!\n");
        return false;
    }

    //将索引设置为释放状态
    clib_memory_static_fixed_pool_set_index_free(pool->used_info, free_index);

    pool->current_item_count -= 1;

    return true;
}


size_t
clib_memory_static_fixed_pool_get_max_item_count(struct clib_memory_static_fixed_pool *pool) {
    clib_check_if_true_return_value(pool == NULL, 0);
    pthread_rwlock_rdlock(&pool->lock);
    size_t result = pool->max_item_count;
    pthread_rwlock_unlock(&pool->lock);
    return result;
}

size_t
clib_memory_static_fixed_pool_get_item_size(struct clib_memory_static_fixed_pool *pool) {
    clib_check_if_true_return_value(pool == NULL, 0);
    pthread_rwlock_rdlock(&pool->lock);
    size_t result = pool->per_item_data_size;
    pthread_rwlock_unlock(&pool->lock);
    return result;
}

size_t
clib_memory_static_fixed_pool_get_current_item_count(struct clib_memory_static_fixed_pool *pool) {
    clib_check_if_true_return_value(pool == NULL, 0);
    pthread_rwlock_rdlock(&pool->lock);
    size_t result = pool->current_item_count;
    pthread_rwlock_unlock(&pool->lock);
    return result;
}

void
clib_memory_static_fixed_pool_clear(struct clib_memory_static_fixed_pool *pool) {
    clib_check_if_true_return(pool == NULL);
    pthread_rwlock_wrlock(&pool->lock);
    //清除使用信息
    if (pool->used_info_size != 0) {
        memset(pool->used_info, 0, pool->used_info_size);
    }
    //重置元素个数
    pool->current_item_count = 0;
    pthread_rwlock_unlock(&pool->lock);
}

void clib_memory_static_fixed_pool_exit(struct clib_memory_static_fixed_pool *pool) {
    clib_check_if_true_return(pool == NULL);
    clib_memory_static_fixed_pool_clear(pool);
    if (pool->is_alloc) {
        clib_memory_allocator_free_default(pool);
    }
}

